package processor;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

/**
 * 
 * @author Austin Bennesh, Charlie Hatcher
 *
 */
public class Simulator {
	private Methods myMethods;
	private Map<Integer, String> opCode;

	private Scanner userInput;

	public Simulator(boolean navSeq, String fileName) {
		opCode = new HashMap<Integer, String>();
		fillMap();
		userInput = new Scanner(System.in);
		myMethods = new Methods();
		myMethods.programCounter = 0;
		readFile(fileName);
		if (navSeq)
			navigateSeq();
		else
			navigateAll();
	}

	public void navigateAll() {
		while (!myMethods.HF)
			executeCommand();
	}

	public void navigateSeq() {
		while (true)
			handleInput();
	}

	public void executeCommand() {
		if (!myMethods.HF) {
			String command = myMethods.myMemory[myMethods.programCounter];
			String opcode = opCode.get(Integer.parseInt(
					command.substring(0, 2), 16));
			command = command.substring(2);
			myMethods.callMethod(opcode, command);

		} else {
			System.err
			.println("Attempted to run more lines than were left in execution.");
		myMethods.HF = true;
	}
}

public void handleInput() {
	System.out.print(">:");
	String[] userCommand = userInput.nextLine().split(" ");

	if (userCommand[0].equals("s") && userCommand.length == 2) {
		for (int i = 0; i < Integer.parseInt(userCommand[1].trim())
		&& !myMethods.HF; i++)
			executeCommand();
	} else if (userCommand[0].equals("q")) {
		System.out.println("Quitting.");
		System.exit(0);
	} else if (userCommand[0].equals("c")) {
		System.out.println("Sign Flag = " + myMethods.SF + ".\n"
				+ "Over flow flag = " + myMethods.OF + ".\n"
				+ "Zero Flag = " + myMethods.ZF);
	} else if (userCommand.length == 2 && userCommand[0].equals("p")
			&& userCommand[1].equals("all")) {
		for (int i = 0; i < 8; i++)
			System.out.println("Contents of register " + i + " : 0x"
					+ Integer.toHexString(myMethods.myRegisters[i]));
		System.out.println("PC is 0x"
				+ Integer.toHexString(myMethods.programCounter));
	} else if (userCommand.length == 2 && userCommand[0].equals("p")) {
		try {
			System.out.println("Contents of register "
					+ userCommand[1]
					              + " : "
					              + Integer.toHexString(myMethods.myRegisters[Integer
					                                                          .parseInt(userCommand[1])]));
		} catch (NumberFormatException e) {
			System.err
			.println("Please enter a valid number for the registry.");
		} catch (NullPointerException e) {
			System.err.println("Register requested is not filled.");
		}
	} else if (userCommand.length == 2 && userCommand[0].equals("d")) {
		try {
			System.out.println("Contents of memory "
					+ userCommand[1]
					              + " : "
					              + (myMethods.myMemory[(Integer.parseInt(userCommand[1],
					            		  16))]));
		} catch (NumberFormatException e) {
			System.err
			.println("Please enter a valid number for the memory location.");
		} catch (IndexOutOfBoundsException e) {
			System.err.println("Memory requested is out of bounds.");
		}
	} else {
		System.err.println("Invalid input. Valid commands: \n"
				+ "   p reg(int 0-7)\n" + "   p all\n"
				+ "   d addr(hex val)\n" + "   s n(int lines to execute)\n"
				+ "   q (quit)\n" + "   c (condition codes");
	}
}

public void readFile(String filePath) {
	try {
		Scanner in = new Scanner(new File(filePath));
		while (in.hasNext()) {
			String hexCall = in.nextLine();
			String[] hexSplit = hexCall.split(":");
			String address = hexSplit[0].substring(2);
			if (hexSplit.length > 1) {
				String memVal = hexSplit[1].trim();
				myMethods.myMemory[Integer.parseInt(address, 16)] = memVal;
			}
		}
	} catch (FileNotFoundException e) {
		System.err.println("File: " + filePath + " not found");
	}
}

private void fillMap() {
	int halt = 0x00;
	int nop = 0x10;
	int rrmovl = 0x20;
	int cmovle = 0x21;
	int cmovl = 0x22;
	int cmove = 0x23;
	int cmovne = 0x24;
	int cmovge = 0x25;
	int cmovg = 0x26;
	int irmovl = 0x30;
	int rmmovl = 0x40;
	int mrmovl = 0x50;
	int addl = 0x60;
	int subl = 0x61;
	int andl = 0x62;
	int xorl = 0x63;
	int jmp = 0x70;
	int jle = 0x71;
	int jl = 0x72;
	int je = 0x73;
	int jne = 0x74;
	int jge = 0x75;
	int jg = 0x76;
	int call = 0x80;
	int ret = 0x90;
	int pushl = 0xa0;
	int popl = 0xb0;
	opCode.put(halt, "halt");
	opCode.put(nop, "nop");
	opCode.put(rrmovl, "rrmovl");
	opCode.put(cmovle, "cmovle");
	opCode.put(cmovl, "cmovl");
	opCode.put(cmove, "cmove");
	opCode.put(cmovne, "cmovne");
	opCode.put(cmovge, "cmovege");
	opCode.put(cmovg, "cmovg");
	opCode.put(irmovl, "irmovl");
	opCode.put(rmmovl, "rmmovl");
	opCode.put(mrmovl, "mrmovl");
	opCode.put(addl, "addl");
	opCode.put(subl, "subl");
	opCode.put(andl, "andl");
	opCode.put(xorl, "xorl");
	opCode.put(jmp, "jmp");
	opCode.put(jle, "jle");
	opCode.put(jl, "jl");
	opCode.put(je, "je");
	opCode.put(jne, "jne");
	opCode.put(jge, "jge");
	opCode.put(jg, "jg");
	opCode.put(call, "call");
	opCode.put(ret, "ret");
	opCode.put(pushl, "pushl");
	opCode.put(popl, "popl");

}

public static void main(String args[]) {
	new Simulator(true, "processor/bintest.txt");
	}

}